package com.g0dkar.leet.core.model;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.g0dkar.leet.exception.LeetRuntimeException;
import com.g0dkar.leet.util.ognl.OgnlUtils;
import com.g0dkar.leet.util.reflection.ReflectionUtils;

/**
 * Mapping of a basic model entity.
 * 
 * @author Daniel
 * @author g0dkar
 */
public class ClassMapping implements Cloneable, Mapping {
	private String name;
	private String displayName;
	private String className;
	private String superClassName;
	private String idField;
	private boolean deletable;
	
	private Class<?> type;
	private Map<String, Property> properties;
	
	private List<Role> writePermissions;
	private List<Role> readPermissions;
	
	public ClassMapping() {
		properties = new LinkedHashMap<String, Property>();
	}
	
	/**
	 * @return Name of this class-mapping.
	 */
	public String getName() {
		return name;
	}
	
	public String getNameAndClass() {
		return String.format("%s (%s)", name, className);
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getDisplayName() {
		return displayName != null ? displayName : name;
	}
	
	public void setDisplayName(String displayName) {
		this.displayName = displayName;
	}
	
	/**
	 * @return Fully qualified name of this entity's class.
	 */
	public String getClassName() {
		return className;
	}
	
	public void setClassName(String className) {
		this.className = className;
	}
	
	/**
	 * @return Fully qualified name of the super class of this entity.
	 */
	public String getSuperClassName() {
		return superClassName;
	}
	
	public void setSuperClassName(String superClassName) {
		this.superClassName = superClassName;
	}
	
	/**
	 * @return Property used as persistence id.
	 */
	public String getIdField() {
		return idField;
	}
	
	public void setIdField(String idField) {
		this.idField = idField;
	}
	
	/**
	 * @return true if instances of this entity can be deleted.
	 */
	public boolean isDeletable() {
		// Nothing can be deleted. Ever.
		return deletable && false;
	}
	
	public void setDeletable(boolean deletable) {
		// Nothing can be deleted. Ever.
		this.deletable = deletable && false;
	}
	
	/**
	 * @return Attributes of this entity.
	 */
	public Map<String, Property> getProperties() {
		return properties;
	}
	
	public void setProperties(Map<String, Property> attributes) {
		properties = attributes;
	}
	
	/**
	 * Returns the actual {@link Class} of this MetaClass, loading if it isn't loaded yet.
	 * 
	 * @return Actual class of this MetaClass.
	 */
	public Class<?> getType() {
		if (type == null) {
			try {
				type = Class.forName(className);
			} catch (ClassNotFoundException e) {
				throw new LeetRuntimeException(e);
			}
		}
		
		return type;
	}
	
	public void setType(Class<?> type) {
		this.type = type;
	}
	
	/**
	 * @return The roles authorized to insert new data and update existing items.
	 */
	public List<Role> getWritePermissions() {
		return writePermissions;
	}
	
	public void setWritePermissions(List<Role> writePermissions) {
		this.writePermissions = writePermissions;
	}
	
	/**
	 * @return The roles authorized to read and display listings and detailed info.
	 */
	public List<Role> getReadPermissions() {
		return readPermissions;
	}
	
	public void setReadPermissions(List<Role> readPermissions) {
		this.readPermissions = readPermissions;
	}
	
	/**
	 * Adds given MetaProperty to this MetaClass' attribute map.
	 * 
	 * @param attribute
	 */
	public void add(Property attribute) {
		properties.put(attribute.getName(), attribute);
	}
	
	/**
	 * Attempts to create a new instance of this entity, returning null if it can't.
	 * 
	 * @param params
	 * @return a new instance of this entity.
	 */
	public Object newInstance(Object... constructorParams) {
		return ReflectionUtils.newInstance(getType(), constructorParams);
	}
	
	/**
	 * Attempts to create a new instance of this entity filling it with the value map, returning null if it can't.
	 * 
	 * @param params
	 * @param 
	 * @return a new instance of this entity.
	 */
	public Object newInstance(Map<String, Object> values, Object... constructorParams) {
		Object newObject = newInstance(constructorParams);
		OgnlUtils.fillObject(newObject, values);
		
		return newObject;
	}
	
	/**
	 * Defines equality between two ClassMappings as equality of name and className.
	 */
	public boolean equals(Object obj) {
		if (obj instanceof ClassMapping) {
			ClassMapping o = (ClassMapping) obj;
			
			// Same Name and ClassName
			if (getName().equals(o.getName()) && getClassName().equals(o.getClassName())) {
				return true;
			}
		}
		
		return false;
	}
	
	public Object buildObject(Map<String, Object> values) {
		return OgnlUtils.buildObject(getType(), values);
	}
}
